package com.wzw.springboot.principle.use.es.raw;

import com.wzw.springboot.principle.use.es.constant.Es;
import com.wzw.springboot.principle.use.json.JacksonHandler;
import org.apache.http.HttpHost;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.indices.GetIndexResponse;
import org.elasticsearch.cluster.metadata.AliasMetadata;
import org.elasticsearch.cluster.metadata.MappingMetadata;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;

import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Locale;
import java.util.Map;

/**
 * 原生用法
 *
 * @author Wangzhiwen
 */
public class RawApi {

    static final String ID = "8ae98746-aaa2-4cb0-8349-afbefad6185c";

    interface Consumer<T> {
        void accept(T t) throws IOException;
    }

    public static void template(Consumer<RestHighLevelClient> consumer) throws IOException {
        // 构建客户端
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient
                        .builder(new HttpHost(Es.DEFAULT_HOST, Es.DEFAULT_PORT, HttpHost.DEFAULT_SCHEME_NAME))
        );
        consumer.accept(client);
        client.close();
    }

    // 1.创建索引 索引已存在时会报错 PUT http://127.0.0.1:9200/raw_index
    public static void createIndex() throws IOException {
        template(client -> {
            CreateIndexRequest createIndexRequest = new CreateIndexRequest(Es.RAW_INDEX);
            // createIndexRequest.mapping();
            // createIndexRequest.alias()
            CreateIndexResponse createIndexResponse = client.indices().create(createIndexRequest, RequestOptions.DEFAULT);
            boolean acknowledged = createIndexResponse.isAcknowledged();
            System.out.println("创建索引结果: " + acknowledged);
        });
    }

    // 2.查看指定索引信息 GET http://127.0.0.1:9200/raw_index
    public static void getIndices() throws IOException {
        template(client -> {
            GetIndexRequest getIndexRequest = new GetIndexRequest(Es.RAW_INDEX);
            GetIndexResponse getIndexResponse = client.indices().get(getIndexRequest, RequestOptions.DEFAULT);
            String[] indices = getIndexResponse.getIndices(); // 索引名称
            Map<String, List<AliasMetadata>> aliases = getIndexResponse.getAliases(); // 别名
            Map<String, MappingMetadata> mappings = getIndexResponse.getMappings(); // 映射
            Map<String, Settings> settings = getIndexResponse.getSettings(); // 索引-设置
        });
    }

    // 3.删除索引 DELETE http://127.0.0.1:9200/raw_index
    public static void deleteIndex() throws IOException {
        template(client -> {
            DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(Es.RAW_INDEX);
            AcknowledgedResponse acknowledgedResponse = client.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
            boolean acknowledged = acknowledgedResponse.isAcknowledged(); // 结果标识
            System.out.println("删除ES索引结果: " + acknowledged);
        });
    }

    // 1.新增文档 POST http://127.0.0.1:9200/raw_index/_doc/id
    public static void postDocument() throws IOException {
        template(client -> {
            IndexRequest indexRequest = new IndexRequest(Es.RAW_INDEX);
            // 设置ID
            indexRequest.id(ID);
            // 创建数据源 source
            RawIndex rawIndex = new RawIndex()
                    .setId(ID).setAge(18).setHigh(BigDecimal.valueOf(526.23D))
                    .setName("wangzhiwen").setIsDeleted(false)
                    .setCreateTime(LocalDateTime.now()).setUpdateTime(LocalDateTime.now());
            // 如过直接传入对象，内部是调用了toString方法转成字符串
            // indexRequest.source(XContentType.JSON, rawIndex);
            indexRequest.source(JacksonHandler.toJsonString(rawIndex), XContentType.JSON);
            IndexResponse indexResponse = client.index(indexRequest, RequestOptions.DEFAULT);
            // 操作结果枚举
            DocWriteResponse.Result result = indexResponse.getResult();
            String id = indexResponse.getId(); // 返回的ID，如果传入时不设置ES会生成一个默认的ID。
            System.out.println("Result : " + result.getLowercase());
        });
    }

    // 2.修改文档内容 PUT http://127.0.0.1/raw_index/id
    public static void putDocument() throws IOException {
        template(client -> {
            UpdateRequest updateRequest = new UpdateRequest();
            updateRequest.index(Es.RAW_INDEX).id(ID);
            updateRequest.doc(XContentType.JSON, "name", "wangzhiwen-update");
            UpdateResponse updateResponse = client.update(updateRequest, RequestOptions.DEFAULT);
            String id = updateResponse.getId();
            DocWriteResponse.Result result = updateResponse.getResult();
            long version = updateResponse.getVersion();
            System.out.println("Update Result : " + result.getLowercase());
        });
    }

    // 3.根据ID查询文档 GET http://127.0.0.1/raw_index/id
    public static void getDocument() throws IOException {
        template(client -> {
            GetRequest getRequest = new GetRequest(Es.RAW_INDEX);
            getRequest.id(ID);
            GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);
            String id = getResponse.getId();
            Map<String, Object> source = getResponse.getSource();
            System.out.println("Get Result : " + source);
        });
    }

    // 4.根据ID删除文档 DELETE http://127.0.0.1/raw_index/1
    public static void deleteDocument() throws IOException {
        template(client -> {
            DeleteRequest deleteRequest = new DeleteRequest(Es.RAW_INDEX);
            deleteRequest.id(ID);
            DeleteResponse deleteResponse = client.delete(deleteRequest, RequestOptions.DEFAULT);
            String id = deleteResponse.getId();
            DocWriteResponse.Result result = deleteResponse.getResult();
            System.out.println("Delete Result : " + result.getLowercase());
        });
    }

    // 5.批量新增
    public static void batchDocument() throws IOException {
        template(client -> {
            // 批量请求体 内部包单个请求体
            BulkRequest bulkRequest = new BulkRequest();
            bulkRequest.add(new IndexRequest(Es.RAW_INDEX).id("1").source(XContentType.JSON, "name", "name1", "age", 51));
            bulkRequest.add(new IndexRequest(Es.RAW_INDEX).id("2").source(XContentType.JSON, "name", "name2", "age", 52));
            bulkRequest.add(new IndexRequest(Es.RAW_INDEX).id("3").source(XContentType.JSON, "name", "name3", "age", 53));
            bulkRequest.add(new IndexRequest(Es.RAW_INDEX).id("4").source(XContentType.JSON, "name", "name4", "age", 54));
            bulkRequest.add(new IndexRequest(Es.RAW_INDEX).id("5").source(XContentType.JSON, "name", "name5", "age", 55));
            bulkRequest.add(new IndexRequest(Es.RAW_INDEX).id("6").source(XContentType.JSON, "name", "name6", "age", 56));
            BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
            RestStatus status = bulkResponse.status();
            System.out.println(status.name().toUpperCase(Locale.ROOT));
            TimeValue took = bulkResponse.getTook();
            long millis = took.millis(); // 花费的时间
            // 批量更新
            bulkRequest.add(new UpdateRequest().index(Es.RAW_INDEX).id("1").doc(XContentType.JSON, "name", "name1-update"));
            // ...
        });
    }

    private static void printSearchResult(SearchResponse searchResponse) {
        TimeValue took = searchResponse.getTook(); // 耗时
        SearchHits hits = searchResponse.getHits(); // 命中的结果
        int size = hits.getHits().length;
        for (SearchHit hit : hits) {
            System.out.println("Search Result : " + hit.getSourceAsMap());
            // 高亮字段 fragments[[<font color='red'>wangzhiwen</font>]]}
            System.out.println("Highlight Fields" + hit.getHighlightFields());
        }
        System.out.println("Search Result Size : " + size);
        System.out.println("Search Result Cost : " + took.getMillis());
    }

    // 1.查询所有内全部数据
    public static void searchIndex() throws IOException {
        template(client -> {
            // 创建搜索请求体
            SearchRequest searchRequest = new SearchRequest(Es.RAW_INDEX);
            // 设置请求参数
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchSourceBuilder.query(QueryBuilders.matchAllQuery());
            searchRequest.source(searchSourceBuilder);
            // 查询
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            printSearchResult(searchResponse);
        });
    }

    // 2.关键字查询
    public static void termSearch() throws IOException {
        template(client -> {
            SearchRequest searchRequest = new SearchRequest(Es.RAW_INDEX);
            SearchSourceBuilder sourceBuilder = SearchSourceBuilder.searchSource().query(QueryBuilders.termQuery("name", "wangzhiwen"));
            searchRequest.source(sourceBuilder);
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            printSearchResult(searchResponse);
        });
    }

    // 3.分页查询
    public static void pageSearch() throws IOException {
        template(client -> {
            SearchRequest searchRequest = new SearchRequest(Es.RAW_INDEX);
            SearchSourceBuilder searchSourceBuilder = SearchSourceBuilder.searchSource()
                    .query(QueryBuilders.matchAllQuery())
                    .from(0) // 当前页其实索引(第一条数据的顺序号) 从0开始
                    .size(4); // 每页记录数
            searchRequest.source(searchSourceBuilder);
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            printSearchResult(searchResponse);
        });
    }

    // 4.数据排序
    public static void sort() throws IOException {
        template(client -> {
            SearchRequest searchRequest = new SearchRequest(Es.RAW_INDEX);
            SearchSourceBuilder builder = SearchSourceBuilder.searchSource().query(QueryBuilders.matchAllQuery())
                    .sort("age", SortOrder.ASC); // 根据某字段排序 字符串字段不支持排序？？？
            searchRequest.source(builder);
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            printSearchResult(searchResponse);
        });
    }

    // 5.过滤字段
    public static void filterField() throws IOException {
        template(client -> {
            SearchRequest searchRequest = new SearchRequest(Es.RAW_INDEX);
            SearchSourceBuilder builder = SearchSourceBuilder.searchSource()
                    .query(QueryBuilders.matchAllQuery());
            // 过滤字段 包括/不包括
            builder.fetchSource("name", "age");
            searchRequest.source(builder);
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            printSearchResult(searchResponse);
        });
    }

    // 6.条件查询 ---> 动态查询时可用
    public static void boolSearch() throws IOException {
        template(client -> {
            SearchRequest searchRequest = new SearchRequest(Es.RAW_INDEX);
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            // 必须包括
            boolQueryBuilder.must(QueryBuilders.matchQuery("name", "wang"));
            // 一定不包括
            boolQueryBuilder.mustNot(QueryBuilders.matchQuery("age", 51));
            // 可能包括
            boolQueryBuilder.should(QueryBuilders.matchQuery("sex", "man"));
            SearchSourceBuilder query = SearchSourceBuilder.searchSource().query(boolQueryBuilder);
            searchRequest.source(query);
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            printSearchResult(searchResponse);
        });
    }

    // 7.范围查询
    public static void rangeSearch() throws IOException {
        template(client -> {
            SearchRequest request = new SearchRequest(Es.RAW_INDEX);
            RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("age");
            rangeQuery.gte(10); // 大于等于
            rangeQuery.lte(60); // 小于等于
            SearchSourceBuilder builder = SearchSourceBuilder.searchSource().query(rangeQuery);
            request.source(builder);
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            printSearchResult(response);
        });
    }

    // 8.模糊查询
    public static void likeSearch() throws IOException {
        template(client -> {
            SearchRequest request = new SearchRequest(Es.RAW_INDEX);
            request.source(
                    SearchSourceBuilder.searchSource()
                            .query(
                                    QueryBuilders.fuzzyQuery("name", "wangzhiwen").fuzziness(Fuzziness.AUTO)
                            )
            );
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            printSearchResult(response);
        });
    }

    // 9.高亮查询
    public static void highlightSearch() throws IOException {
        template(client -> {
            SearchRequest request = new SearchRequest(Es.RAW_INDEX);
            SearchSourceBuilder searchSourceBuilder = SearchSourceBuilder.searchSource().query(QueryBuilders.termQuery("name", "wangzhiwen"));
            HighlightBuilder builder = SearchSourceBuilder.highlight()
                    .preTags("<font color='red'>") // 高亮体前缀
                    .postTags("</font>") // 高亮体后缀
                    .field("name"); // 需要高亮的字段
            searchSourceBuilder.highlighter(builder);
            request.source(searchSourceBuilder);
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            printSearchResult(response);
        });
    }

    // 10.聚合查询
    public static void aggregationSearch() throws IOException {
        template(client -> {
            SearchRequest request = new SearchRequest(Es.RAW_INDEX);
            SearchSourceBuilder searchSourceBuilder = SearchSourceBuilder.searchSource();
            // max()
            searchSourceBuilder.aggregation(AggregationBuilders.max("maxAge").field("age"));
            request.source(searchSourceBuilder);
            printSearchResult(client.search(request, RequestOptions.DEFAULT));
            // mix()
            searchSourceBuilder.aggregation(AggregationBuilders.min("minAge").field("age"));
            // groupBy() 按关键字分组
            searchSourceBuilder.aggregation(AggregationBuilders.terms("groupAge").field("age"));
            //
        });
    }

    // 11.多条件动态查询


    public static void main(String[] args) throws IOException {
        // 索引操作
        // createIndex();
        // getIndices();
        // deleteIndex();

        // 文档记录操作 _doc
        // postDocument();
        // putDocument();
        // getDocument();
        // batchDocument();

        // 条件查询 _search
        // searchIndex();
        // termSearch();
        // pageSearch();
        // sort();
        // filterField();
        // boolSearch();
        // rangeSearch();
        // likeSearch();
        highlightSearch();
    }

}
